# Load necessary packages. 
# Basic Data Analysis & Wrangling
library(tidyverse)
library(lubridate)
# Library for splitting the chinese words. 
library(jiebaRD)
library(jiebaR)
# Library for generating word cloud. 
library(wordcloud)
# Library for visualizing the 3d plot. 
library(plotly)

I - Introduction

II - Data Preprocessing

Load datasets

dt_issues = read.csv("data/issues_data.csv", header=TRUE)
dt_star = read.csv("data/stargazers.csv", header=TRUE)
dt_user = read.csv("data/users_data.csv", header=TRUE)

Saniety Check

# Inspect the dataset by taking the first 10 rows of each dataset. 
dt_issues %>% head(10)
dt_star %>% head(10)
dt_user %>% head(10)

Data Cleaning & Wrangling

# User dataset cleaning
# Cleaning
dt_user_cld <- dt_user %>% 
    # Join the issues dataset. 
    left_join(dt_issues, by="X_id") %>%
    select(bio, blog, company, created_at.x, followers, following, hireable, location, login, name, public_gists, 
           type, closed_at, updated_at.x, email, organizations_url, public_repos) %>%
    # Drop unused features. 
    # select(-X_id, -avatar_url, -events_url, -followers_url, -following_url, 
    #        -gists_url, -gravatar_id, -html_url, -node_id, -public_gists, -received_events_url, 
    #        -repos_url, -site_admin, -starred_url, -subscriptions_url, -type) %>%
    # Convert the time/date features to relative format. 
    mutate(created_at = lubridate::ymd_hms(created_at.x), 
           updated_at = lubridate::ymd_hms(updated_at.x)) %>%
    # Convert various factor type features to string type. 
    mutate(bio = as.character(bio), 
           blog = as.character(blog), 
           company = as.character(company), 
           email = as.character(email), 
           location = as.character(location), 
           login = as.character(login), 
           name = as.character(name), 
           organizations_url = as.character(organizations_url))
Column `X_id` joining factors with different levels, coercing to character vector
str(dt_user_cld)
'data.frame':   39987 obs. of  19 variables:
 $ bio              : chr  "" "" "" "" ...
 $ blog             : chr  "" "" "" "" ...
 $ company          : chr  "" "" "" "" ...
 $ created_at.x     : Factor w/ 39971 levels "2008-03-26T03:33:42Z",..: 23111 24348 13701 27767 34585 17517 19613 22254 23043 18383 ...
 $ followers        : int  9 4 7 2 0 2 13 5 1 0 ...
 $ following        : int  16 38 14 89 0 0 21 126 48 1 ...
 $ hireable         : Factor w/ 2 levels "","True": 1 1 1 1 1 1 1 1 1 1 ...
 $ location         : chr  "" "" "" "" ...
 $ login            : chr  "moloach" "bhxch" "YueNing" "BigFaceCatMhc" ...
 $ name             : chr  "" "Zhe Lee" "naodongbanana" "" ...
 $ public_gists     : int  0 0 0 0 0 0 1 24 0 0 ...
 $ type             : Factor w/ 1 level "User": 1 1 1 1 1 1 1 1 1 1 ...
 $ closed_at        : logi  NA NA NA NA NA NA ...
 $ updated_at.x     : Factor w/ 38705 levels "2015-10-22T09:47:56Z",..: 14522 14681 25825 31448 31450 37456 23803 31101 4862 19705 ...
 $ email            : chr  "" "mytempbh@outlook.com" "n1085633848@outlook.com" "" ...
 $ organizations_url: chr  "https://api.github.com/users/moloach/orgs" "https://api.github.com/users/bhxch/orgs" "https://api.github.com/users/YueNing/orgs" "https://api.github.com/users/BigFaceCatMhc/orgs" ...
 $ public_repos     : int  10 34 29 4 0 26 93 102 13 2 ...
 $ created_at       : POSIXct, format: "2016-07-12 05:17:50" "2016-08-27 14:04:23" "2015-06-19 13:57:11" "2017-01-14 03:30:31" ...
 $ updated_at       : POSIXct, format: "2019-03-09 00:15:05" "2019-03-09 09:30:45" "2019-03-25 10:50:50" "2019-03-28 00:02:38" ...
# Issues dataset cleaning
dt_issues_cld <- dt_issues %>%
    # Drop unused features. 
    select(created_at, body, comments, created_at, title, user.login) %>%
    # Convert the time/date features to relative format. 
    mutate(created_at = lubridate::ymd_hms(created_at), 
           # Convert the factor type features to the correct format. 
           body = as.character(body), 
           title = as.character(title), 
           user.login = as.character(user.login))

III - Data Analysis

1. Supporters’ Profile Analysis

1. What Companies/Universities are those programmers from?

company_info <- dt_user_cld %>% 
    group_by(company) %>%
    summarise(count = n()) %>%
    arrange(desc(count)) %>%
    filter(company != "")
# Display the top companies. 
company_info
# Define the company aggregation function. 
company_aggregation <- function(name) {
    # Make case insensitive. 
    orig_name <- name
    name <- toupper(name)
    # Detect pattern and change the company name accordingly. 
    if (grepl("百度|BAIDU|AIDU", name)) {
        target_name <- "Baidu"
    } else if (grepl("ENCENT|腾讯|TENCENT", name)) {
        target_name <- "Tencent"
    } else if (grepl("LIBABA|淘宝|AOBAO|LIPAY|阿里巴巴|LIYUN|阿里云", name)) {
        target_name <- "Alibaba"
    } else if (grepl("JD|京东", name)) {
        target_name <- "JD"
    } else if (grepl("ETEASE|网易", name)) {
        target_name <- "NetEase"
    } else if (grepl("EITUAN|美团", name)) {
        target_name <- "MeiTuan"
    } else if (grepl("YTEDANCE|字节|头条", name)) {
        target_name <- "ByteDance"
    } else if (grepl("ELEME|饿了", name)) {
        target_name <- "Eleme"
    } else if (grepl("UAWEI|华为", name)) {
        target_name <- "Huawei"
    } else if (grepl("DIDI|滴滴|嘀嘀", name)) {
        target_name <- "DiDi"
    } else {
        target_name <- orig_name
    }
    
    return (target_name)
}
# Define the education aggregation function. 
education_aggregation <- function(name) {
    # Make case insensitive
    orig_name <- name
    name <- toupper(name)
    # Detect pattern and change the education accordingly. 
    if (grepl("HEJIANG|ZJU|浙江大学|浙大", name)) {
        target_name <- "Zhejiang University"
    } else if (grepl("SINGHUA|清华", name)) {
        target_name <- "Tsinghua University"
    } else if (grepl("SHANGHAI JIAO TONG|SJTU|上海交大|上海交通", name)) {
        target_name <- "Shanghai Jiao Tong University"
    } else if (grepl("UESTC|电子科大|电子科技", name)) {
        target_name <- "University of Electronic Science and Technology of China"
    } else if (grepl("USTC|中科大|中国科学技术", name)) {
        target_name <- "University of Science and Technology of China"
    } else if (grepl("FUDAN|复旦", name)) {
        target_name <- "Fudan University"
    } else if (grepl("ARBIN|哈", name)) {
        target_name <- "Harbin Institute of Technology"
    } else if (grepl("BUPT|北邮|北京邮电", name)) {
        target_name <- "Beijing University of Post and Telecommunications"
    } else {
        target_name <- NA
    }
    
    return (target_name)
}
# Aggregating disparse companies. 
agg_companies <- rep(NA, nrow(company_info))
agg_education <- rep(NA, nrow(company_info))
for (i in 1:nrow(company_info)) {
    agg_companies[i] <- company_aggregation(company_info$company[i])
    agg_education[i] <- education_aggregation(company_info$company[i])
}
company_info_agg <- cbind(company_info, agg_companies, agg_education)
# Show the top ten companies which have the most number of developer support 996.icu
company_info_agg %>% group_by(agg_companies) %>%
    summarise(count = n()) %>%
    arrange(desc(count)) %>% 
    head(10)
# Show what universities are those developers from. 
company_info_agg %>% group_by(agg_education) %>%
    summarise(count = n()) %>%
    arrange(desc(count)) %>%
    filter(!is.na(agg_education)) %>%
    head(10)
Factor `agg_education` contains implicit NA, consider using `forcats::fct_explicit_na`
NA

2. What cities are those developers from?

# 
# Define the function for aggregating the cities. 
city_aggregation <- function(name) {
    # Make case insensitive. 
    orig_name <- name
    name <- toupper(name)
    # Detect pattern and change the education accordingly. 
    if (grepl("EIJING|北京", name)) {
        target_name <- "Beijing"
    } else if (grepl("HANGHAI|上海", name)) {
        target_name <- "Shanghai"
    } else if (grepl("ANGZHOU|杭州", name)) {
        target_name <- "Hangzhou"
    } else if (grepl("UANGZHOU|广州", name)) {
        target_name <- "Hangzhou"
    } else if (grepl("HENGDU|成都", name)) {
        target_name <- "Chengdu"
    } else if (grepl("ANJING|南京", name)) {
        target_name <- "Nanjing"
    } else if (grepl("INGAPORE|新加坡", name)) {
        target_name <- "Singapore"
    } else if (grepl("HONG KONG|香港|HK", name)) {
        target_name <- "Hong Kong"
    } else if (grepl("UHAN|武汉", name)) {
        target_name <- "Wuhan"
    } else {
        target_name <- orig_name
    }
    
    return (target_name)
}
city_info <- dt_user_cld %>% 
    group_by(location) %>%
    summarise(count = n()) %>%
    filter(location != "", 
           location != "China") %>%
    arrange(desc(count))
agg_cities <- rep(NA, nrow(city_info))
for (i in 1:nrow(city_info)) {
    agg_cities[i] <- city_aggregation(city_info$location[i])
} 
city_info_agg <- cbind(city_info, agg_cities)
# Showing the top ten cities that have the most developer support 996.icu
city_info_agg %>% group_by(agg_cities) %>%
    summarise(count = n()) %>%
    filter(agg_cities != "", 
           agg_cities != "China") %>%
    arrange(desc(count)) %>%
    head(10)

4. Distribution Plot of Supporters’ Information.

# Distribution graph of supporter's followers under 50. 
dist_ggplot <- dt_user_cld %>% filter(followers <= 50, following <= 50, public_repos <= 50) %>%
    ggplot() +
    geom_bar(aes(x = followers), col="black", fill="black", alpha=0.5) +
    geom_bar(aes(x = following), col="black", fill="red", alpha=0.5) +
    geom_bar(aes(x = public_repos), col="black", fill="blue", alpha=0.5)
    
dist_ggplot + 
    labs(x = "Followers (Black), Following (Red) and Public Repositories (Blue)", 
         y = "Count") +
    ggtitle("Distribution Plot")

5. Distribution Plot of Supporters’ Registration Duration.

# Calculate supporters' number of days since registered the github account. 
today <- lubridate::ymd("2019-04-29")
dt_user_cld <- dt_user_cld %>%
    # Calculate the duration and convert it to numerical value. 
    mutate(duration = as.numeric(as.duration(interval(created_at, today)), "days"))
# Showing average registration years. 
print(mean(dt_user_cld$duration)/365)
[1] 3.321946
# Distribution plot of registration days. 
dt_user_cld %>% ggplot(aes(x = duration/365)) + 
    geom_histogram(col="black", fill="grey", alpha = 0.7) +
    geom_vline(xintercept = mean(dt_user_cld$duration)/365, linetype = "dotted", color = "red", size = 1.5) +
    labs(y = "Frequency / Count", 
         x = "Number of Years Since Registration") +
    ggtitle("Distribution Plot of Supporters' Registration Duration")

2. Statistical Modeling

1. Analyzing the Relationship Between Followers and other factors.

# Select variables for analysis. 
user_stat <- dt_user_cld %>% 
    select(followers, following, public_repos, duration)
# Saniety Check
user_stat %>% head(10)
# Unsupervised Learning: PCA
user_pca <- prcomp(user_stat, center=TRUE, scale.=TRUE)
print(user_pca)
Standard deviations (1, .., p=4):
[1] 1.1689728 0.9810532 0.9575395 0.8684211

Rotation (n x k) = (4 x 4):
                   PC1        PC2         PC3        PC4
followers    0.3419542 -0.8727076  0.30951456  0.1601545
following    0.5162926  0.3258117  0.60978132 -0.5054259
public_repos 0.6008709  0.3352248 -0.09115495  0.7199092
duration     0.5054339 -0.1408987 -0.72391868 -0.4479128
summary(user_pca)
Importance of components:
                          PC1    PC2    PC3    PC4
Standard deviation     1.1690 0.9811 0.9575 0.8684
Proportion of Variance 0.3416 0.2406 0.2292 0.1885
Cumulative Proportion  0.3416 0.5822 0.8115 1.0000
# Visualization using 3 principle components PCA
plotly()
You need a plotly username. See help(signup, package = 'plotly')Couldn't find usernameYou need an api_key. See help(signup, package = 'plotly')Couldn't find api_key'plotly' is deprecated.
Use 'ggplotly' instead.
See help("Deprecated")'plotly' is deprecated.
Use 'plot_ly' instead.
See help("Deprecated")

3. Main Questions/Issues of Supporters

2. Text Analysis About Topics of 996 Movement.

# Setting the word-split engine
splitter <- worker(stop_word = "data/stopwords.txt")
# Splitting the words. 
seg <- c(splitter[dt_issues_cld$title], splitter[dt_issues_cld$body])
seg <- seg[nchar(seg) > 1]
# Encode the chinese word vector as UTF-8 format. 
Encoding(seg) <- "UTF-8"
# Extract the top 100 
seg_df <- data.frame(seg = seg) %>%
    group_by(seg) %>%
    summarise(freq = n()) %>%
    arrange(desc(freq)) %>%
    head(100)
# Generating word cloud (Chinese Version). 
font_family <- par("family")
par(family = "Adobe Heiti Std R")
wordcloud(words=seg_df$seg, freq=seg_df$freq, 
          colors=brewer.pal(8,"Dark2"), 
          scale=c(4, 0.8))

# Loading translated dataset. 
trans <- read.table("data/translate", sep="\t")[-1,]
seg_df <- cbind(seg_df, engl = trans$V2)
# Generating word cloud (English Version)
wordcloud(words=seg_df$engl, freq=seg_df$freq, 
          colors=brewer.pal(8,"Dark2"), 
          scale=c(4, 0.8))

LS0tCnRpdGxlOiAiOTk2IEFuYWx5c2lzIgphdXRob3I6ICJaaGVuZyBaaGFuZywgIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KIyBMb2FkIG5lY2Vzc2FyeSBwYWNrYWdlcy4gCiMgQmFzaWMgRGF0YSBBbmFseXNpcyAmIFdyYW5nbGluZwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShsdWJyaWRhdGUpCgojIExpYnJhcnkgZm9yIHNwbGl0dGluZyB0aGUgY2hpbmVzZSB3b3Jkcy4gCmxpYnJhcnkoamllYmFSRCkKbGlicmFyeShqaWViYVIpCgojIExpYnJhcnkgZm9yIGdlbmVyYXRpbmcgd29yZCBjbG91ZC4gCmxpYnJhcnkod29yZGNsb3VkKQoKIyBMaWJyYXJ5IGZvciB2aXN1YWxpemluZyB0aGUgM2QgcGxvdC4gCmxpYnJhcnkocGxvdGx5KQpgYGAKCgojIEkgLSBJbnRyb2R1Y3Rpb24KCgoKIyBJSSAtIERhdGEgUHJlcHJvY2Vzc2luZwoKIyMjIExvYWQgZGF0YXNldHMKCmBgYHtyfQpkdF9pc3N1ZXMgPSByZWFkLmNzdigiZGF0YS9pc3N1ZXNfZGF0YS5jc3YiLCBoZWFkZXI9VFJVRSkKZHRfc3RhciA9IHJlYWQuY3N2KCJkYXRhL3N0YXJnYXplcnMuY3N2IiwgaGVhZGVyPVRSVUUpCmR0X3VzZXIgPSByZWFkLmNzdigiZGF0YS91c2Vyc19kYXRhLmNzdiIsIGhlYWRlcj1UUlVFKQpgYGAKCiMjIyBTYW5pZXR5IENoZWNrCgpgYGB7cn0KIyBJbnNwZWN0IHRoZSBkYXRhc2V0IGJ5IHRha2luZyB0aGUgZmlyc3QgMTAgcm93cyBvZiBlYWNoIGRhdGFzZXQuIApkdF9pc3N1ZXMgJT4lIGhlYWQoMTApCmR0X3N0YXIgJT4lIGhlYWQoMTApCmR0X3VzZXIgJT4lIGhlYWQoMTApCmBgYAoKIyMjIERhdGEgQ2xlYW5pbmcgJiBXcmFuZ2xpbmcKCmBgYHtyfQojIFVzZXIgZGF0YXNldCBjbGVhbmluZwojIENsZWFuaW5nCmR0X3VzZXJfY2xkIDwtIGR0X3VzZXIgJT4lIAogICAgIyBKb2luIHRoZSBpc3N1ZXMgZGF0YXNldC4gCiAgICBsZWZ0X2pvaW4oZHRfaXNzdWVzLCBieT0iWF9pZCIpICU+JQogICAgc2VsZWN0KGJpbywgYmxvZywgY29tcGFueSwgY3JlYXRlZF9hdC54LCBmb2xsb3dlcnMsIGZvbGxvd2luZywgaGlyZWFibGUsIGxvY2F0aW9uLCBsb2dpbiwgbmFtZSwgcHVibGljX2dpc3RzLCAKICAgICAgICAgICB0eXBlLCBjbG9zZWRfYXQsIHVwZGF0ZWRfYXQueCwgZW1haWwsIG9yZ2FuaXphdGlvbnNfdXJsLCBwdWJsaWNfcmVwb3MpICU+JQogICAgIyBEcm9wIHVudXNlZCBmZWF0dXJlcy4gCiAgICAjIHNlbGVjdCgtWF9pZCwgLWF2YXRhcl91cmwsIC1ldmVudHNfdXJsLCAtZm9sbG93ZXJzX3VybCwgLWZvbGxvd2luZ191cmwsIAogICAgIyAgICAgICAgLWdpc3RzX3VybCwgLWdyYXZhdGFyX2lkLCAtaHRtbF91cmwsIC1ub2RlX2lkLCAtcHVibGljX2dpc3RzLCAtcmVjZWl2ZWRfZXZlbnRzX3VybCwgCiAgICAjICAgICAgICAtcmVwb3NfdXJsLCAtc2l0ZV9hZG1pbiwgLXN0YXJyZWRfdXJsLCAtc3Vic2NyaXB0aW9uc191cmwsIC10eXBlKSAlPiUKICAgICMgQ29udmVydCB0aGUgdGltZS9kYXRlIGZlYXR1cmVzIHRvIHJlbGF0aXZlIGZvcm1hdC4gCiAgICBtdXRhdGUoY3JlYXRlZF9hdCA9IGx1YnJpZGF0ZTo6eW1kX2htcyhjcmVhdGVkX2F0LngpLCAKICAgICAgICAgICB1cGRhdGVkX2F0ID0gbHVicmlkYXRlOjp5bWRfaG1zKHVwZGF0ZWRfYXQueCkpICU+JQogICAgIyBDb252ZXJ0IHZhcmlvdXMgZmFjdG9yIHR5cGUgZmVhdHVyZXMgdG8gc3RyaW5nIHR5cGUuIAogICAgbXV0YXRlKGJpbyA9IGFzLmNoYXJhY3RlcihiaW8pLCAKICAgICAgICAgICBibG9nID0gYXMuY2hhcmFjdGVyKGJsb2cpLCAKICAgICAgICAgICBjb21wYW55ID0gYXMuY2hhcmFjdGVyKGNvbXBhbnkpLCAKICAgICAgICAgICBlbWFpbCA9IGFzLmNoYXJhY3RlcihlbWFpbCksIAogICAgICAgICAgIGxvY2F0aW9uID0gYXMuY2hhcmFjdGVyKGxvY2F0aW9uKSwgCiAgICAgICAgICAgbG9naW4gPSBhcy5jaGFyYWN0ZXIobG9naW4pLCAKICAgICAgICAgICBuYW1lID0gYXMuY2hhcmFjdGVyKG5hbWUpLCAKICAgICAgICAgICBvcmdhbml6YXRpb25zX3VybCA9IGFzLmNoYXJhY3Rlcihvcmdhbml6YXRpb25zX3VybCkpCgpzdHIoZHRfdXNlcl9jbGQpCgojIElzc3VlcyBkYXRhc2V0IGNsZWFuaW5nCmR0X2lzc3Vlc19jbGQgPC0gZHRfaXNzdWVzICU+JQogICAgIyBEcm9wIHVudXNlZCBmZWF0dXJlcy4gCiAgICBzZWxlY3QoY3JlYXRlZF9hdCwgYm9keSwgY29tbWVudHMsIGNyZWF0ZWRfYXQsIHRpdGxlLCB1c2VyLmxvZ2luKSAlPiUKICAgICMgQ29udmVydCB0aGUgdGltZS9kYXRlIGZlYXR1cmVzIHRvIHJlbGF0aXZlIGZvcm1hdC4gCiAgICBtdXRhdGUoY3JlYXRlZF9hdCA9IGx1YnJpZGF0ZTo6eW1kX2htcyhjcmVhdGVkX2F0KSwgCiAgICAgICAgICAgIyBDb252ZXJ0IHRoZSBmYWN0b3IgdHlwZSBmZWF0dXJlcyB0byB0aGUgY29ycmVjdCBmb3JtYXQuIAogICAgICAgICAgIGJvZHkgPSBhcy5jaGFyYWN0ZXIoYm9keSksIAogICAgICAgICAgIHRpdGxlID0gYXMuY2hhcmFjdGVyKHRpdGxlKSwgCiAgICAgICAgICAgdXNlci5sb2dpbiA9IGFzLmNoYXJhY3Rlcih1c2VyLmxvZ2luKSkKYGBgCgojIElJSSAtIERhdGEgQW5hbHlzaXMKCiMjIDEuIFN1cHBvcnRlcnMnIFByb2ZpbGUgQW5hbHlzaXMKCiMjIyAxLiBXaGF0IENvbXBhbmllcy9Vbml2ZXJzaXRpZXMgYXJlIHRob3NlIHByb2dyYW1tZXJzIGZyb20/CgpgYGB7cn0KY29tcGFueV9pbmZvIDwtIGR0X3VzZXJfY2xkICU+JSAKICAgIGdyb3VwX2J5KGNvbXBhbnkpICU+JQogICAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSAlPiUKICAgIGFycmFuZ2UoZGVzYyhjb3VudCkpICU+JQogICAgZmlsdGVyKGNvbXBhbnkgIT0gIiIpCgojIERpc3BsYXkgdGhlIHRvcCBjb21wYW5pZXMuIApjb21wYW55X2luZm8KYGBgCgoKYGBge3J9CiMgRGVmaW5lIHRoZSBjb21wYW55IGFnZ3JlZ2F0aW9uIGZ1bmN0aW9uLiAKY29tcGFueV9hZ2dyZWdhdGlvbiA8LSBmdW5jdGlvbihuYW1lKSB7CiAgICAjIE1ha2UgY2FzZSBpbnNlbnNpdGl2ZS4gCiAgICBvcmlnX25hbWUgPC0gbmFtZQogICAgbmFtZSA8LSB0b3VwcGVyKG5hbWUpCiAgICAjIERldGVjdCBwYXR0ZXJuIGFuZCBjaGFuZ2UgdGhlIGNvbXBhbnkgbmFtZSBhY2NvcmRpbmdseS4gCiAgICBpZiAoZ3JlcGwoIueZvuW6pnxCQUlEVXxBSURVIiwgbmFtZSkpIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSAiQmFpZHUiCiAgICB9IGVsc2UgaWYgKGdyZXBsKCJFTkNFTlR86IW+6K6vfFRFTkNFTlQiLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJUZW5jZW50IgogICAgfSBlbHNlIGlmIChncmVwbCgiTElCQUJBfOa3mOWunXxBT0JBT3xMSVBBWXzpmL/ph4zlt7Tlt7R8TElZVU586Zi/6YeM5LqRIiwgbmFtZSkpIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSAiQWxpYmFiYSIKICAgIH0gZWxzZSBpZiAoZ3JlcGwoIkpEfOS6rOS4nCIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIkpEIgogICAgfSBlbHNlIGlmIChncmVwbCgiRVRFQVNFfOe9keaYkyIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIk5ldEVhc2UiCiAgICB9IGVsc2UgaWYgKGdyZXBsKCJFSVRVQU58576O5ZuiIiwgbmFtZSkpIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSAiTWVpVHVhbiIKICAgIH0gZWxzZSBpZiAoZ3JlcGwoIllURURBTkNFfOWtl+iKgnzlpLTmnaEiLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJCeXRlRGFuY2UiCiAgICB9IGVsc2UgaWYgKGdyZXBsKCJFTEVNRXzppb/kuoYiLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJFbGVtZSIKICAgIH0gZWxzZSBpZiAoZ3JlcGwoIlVBV0VJfOWNjuS4uiIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIkh1YXdlaSIKICAgIH0gZWxzZSBpZiAoZ3JlcGwoIkRJREl85ru05ru0fOWYgOWYgCIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIkRpRGkiCiAgICB9IGVsc2UgewogICAgICAgIHRhcmdldF9uYW1lIDwtIG9yaWdfbmFtZQogICAgfQogICAgCiAgICByZXR1cm4gKHRhcmdldF9uYW1lKQp9CgojIERlZmluZSB0aGUgZWR1Y2F0aW9uIGFnZ3JlZ2F0aW9uIGZ1bmN0aW9uLiAKZWR1Y2F0aW9uX2FnZ3JlZ2F0aW9uIDwtIGZ1bmN0aW9uKG5hbWUpIHsKICAgICMgTWFrZSBjYXNlIGluc2Vuc2l0aXZlCiAgICBvcmlnX25hbWUgPC0gbmFtZQogICAgbmFtZSA8LSB0b3VwcGVyKG5hbWUpCiAgICAjIERldGVjdCBwYXR0ZXJuIGFuZCBjaGFuZ2UgdGhlIGVkdWNhdGlvbiBhY2NvcmRpbmdseS4gCiAgICBpZiAoZ3JlcGwoIkhFSklBTkd8WkpVfOa1meaxn+Wkp+WtpnzmtZnlpKciLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJaaGVqaWFuZyBVbml2ZXJzaXR5IgogICAgfSBlbHNlIGlmIChncmVwbCgiU0lOR0hVQXzmuIXljY4iLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJUc2luZ2h1YSBVbml2ZXJzaXR5IgogICAgfSBlbHNlIGlmIChncmVwbCgiU0hBTkdIQUkgSklBTyBUT05HfFNKVFV85LiK5rW35Lqk5aSnfOS4iua1t+S6pOmAmiIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIlNoYW5naGFpIEppYW8gVG9uZyBVbml2ZXJzaXR5IgogICAgfSBlbHNlIGlmIChncmVwbCgiVUVTVEN855S15a2Q56eR5aSnfOeUteWtkOenkeaKgCIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIlVuaXZlcnNpdHkgb2YgRWxlY3Ryb25pYyBTY2llbmNlIGFuZCBUZWNobm9sb2d5IG9mIENoaW5hIgogICAgfSBlbHNlIGlmIChncmVwbCgiVVNUQ3zkuK3np5HlpKd85Lit5Zu956eR5a2m5oqA5pyvIiwgbmFtZSkpIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSAiVW5pdmVyc2l0eSBvZiBTY2llbmNlIGFuZCBUZWNobm9sb2d5IG9mIENoaW5hIgogICAgfSBlbHNlIGlmIChncmVwbCgiRlVEQU585aSN5pemIiwgbmFtZSkpIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSAiRnVkYW4gVW5pdmVyc2l0eSIKICAgIH0gZWxzZSBpZiAoZ3JlcGwoIkFSQklOfOWTiCIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIkhhcmJpbiBJbnN0aXR1dGUgb2YgVGVjaG5vbG9neSIKICAgIH0gZWxzZSBpZiAoZ3JlcGwoIkJVUFR85YyX6YKufOWMl+S6rOmCrueUtSIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIkJlaWppbmcgVW5pdmVyc2l0eSBvZiBQb3N0IGFuZCBUZWxlY29tbXVuaWNhdGlvbnMiCiAgICB9IGVsc2UgewogICAgICAgIHRhcmdldF9uYW1lIDwtIE5BCiAgICB9CiAgICAKICAgIHJldHVybiAodGFyZ2V0X25hbWUpCn0KCiMgQWdncmVnYXRpbmcgZGlzcGFyc2UgY29tcGFuaWVzLiAKYWdnX2NvbXBhbmllcyA8LSByZXAoTkEsIG5yb3coY29tcGFueV9pbmZvKSkKYWdnX2VkdWNhdGlvbiA8LSByZXAoTkEsIG5yb3coY29tcGFueV9pbmZvKSkKZm9yIChpIGluIDE6bnJvdyhjb21wYW55X2luZm8pKSB7CiAgICBhZ2dfY29tcGFuaWVzW2ldIDwtIGNvbXBhbnlfYWdncmVnYXRpb24oY29tcGFueV9pbmZvJGNvbXBhbnlbaV0pCiAgICBhZ2dfZWR1Y2F0aW9uW2ldIDwtIGVkdWNhdGlvbl9hZ2dyZWdhdGlvbihjb21wYW55X2luZm8kY29tcGFueVtpXSkKfQpjb21wYW55X2luZm9fYWdnIDwtIGNiaW5kKGNvbXBhbnlfaW5mbywgYWdnX2NvbXBhbmllcywgYWdnX2VkdWNhdGlvbikKYGBgCgoKYGBge3J9CiMgU2hvdyB0aGUgdG9wIHRlbiBjb21wYW5pZXMgd2hpY2ggaGF2ZSB0aGUgbW9zdCBudW1iZXIgb2YgZGV2ZWxvcGVyIHN1cHBvcnQgOTk2LmljdQpjb21wYW55X2luZm9fYWdnICU+JSBncm91cF9ieShhZ2dfY29tcGFuaWVzKSAlPiUKICAgIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lCiAgICBhcnJhbmdlKGRlc2MoY291bnQpKSAlPiUgCiAgICBoZWFkKDEwKQoKIyBTaG93IHdoYXQgdW5pdmVyc2l0aWVzIGFyZSB0aG9zZSBkZXZlbG9wZXJzIGZyb20uIApjb21wYW55X2luZm9fYWdnICU+JSBncm91cF9ieShhZ2dfZWR1Y2F0aW9uKSAlPiUKICAgIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lCiAgICBhcnJhbmdlKGRlc2MoY291bnQpKSAlPiUKICAgIGZpbHRlcighaXMubmEoYWdnX2VkdWNhdGlvbikpICU+JQogICAgaGVhZCgxMCkKICAgIApgYGAKCiMjIyAyLiBXaGF0IGNpdGllcyBhcmUgdGhvc2UgZGV2ZWxvcGVycyBmcm9tPyAKCgpgYGB7cn0KIyAKIyBEZWZpbmUgdGhlIGZ1bmN0aW9uIGZvciBhZ2dyZWdhdGluZyB0aGUgY2l0aWVzLiAKY2l0eV9hZ2dyZWdhdGlvbiA8LSBmdW5jdGlvbihuYW1lKSB7CiAgICAjIE1ha2UgY2FzZSBpbnNlbnNpdGl2ZS4gCiAgICBvcmlnX25hbWUgPC0gbmFtZQogICAgbmFtZSA8LSB0b3VwcGVyKG5hbWUpCiAgICAjIERldGVjdCBwYXR0ZXJuIGFuZCBjaGFuZ2UgdGhlIGVkdWNhdGlvbiBhY2NvcmRpbmdseS4gCiAgICBpZiAoZ3JlcGwoIkVJSklOR3zljJfkuqwiLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJCZWlqaW5nIgogICAgfSBlbHNlIGlmIChncmVwbCgiSEFOR0hBSXzkuIrmtbciLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJTaGFuZ2hhaSIKICAgIH0gZWxzZSBpZiAoZ3JlcGwoIkFOR1pIT1V85p2t5beeIiwgbmFtZSkpIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSAiSGFuZ3pob3UiCiAgICB9IGVsc2UgaWYgKGdyZXBsKCJVQU5HWkhPVXzlub/lt54iLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJIYW5nemhvdSIKICAgIH0gZWxzZSBpZiAoZ3JlcGwoIkhFTkdEVXzmiJDpg70iLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJDaGVuZ2R1IgogICAgfSBlbHNlIGlmIChncmVwbCgiQU5KSU5HfOWNl+S6rCIsIG5hbWUpKSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gIk5hbmppbmciCiAgICB9IGVsc2UgaWYgKGdyZXBsKCJJTkdBUE9SRXzmlrDliqDlnaEiLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJTaW5nYXBvcmUiCiAgICB9IGVsc2UgaWYgKGdyZXBsKCJIT05HIEtPTkd86aaZ5rivfEhLIiwgbmFtZSkpIHsKICAgICAgICB0YXJnZXRfbmFtZSA8LSAiSG9uZyBLb25nIgogICAgfSBlbHNlIGlmIChncmVwbCgiVUhBTnzmrabmsYkiLCBuYW1lKSkgewogICAgICAgIHRhcmdldF9uYW1lIDwtICJXdWhhbiIKICAgIH0gZWxzZSB7CiAgICAgICAgdGFyZ2V0X25hbWUgPC0gb3JpZ19uYW1lCiAgICB9CiAgICAKICAgIHJldHVybiAodGFyZ2V0X25hbWUpCn0KCmNpdHlfaW5mbyA8LSBkdF91c2VyX2NsZCAlPiUgCiAgICBncm91cF9ieShsb2NhdGlvbikgJT4lCiAgICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICU+JQogICAgZmlsdGVyKGxvY2F0aW9uICE9ICIiLCAKICAgICAgICAgICBsb2NhdGlvbiAhPSAiQ2hpbmEiKSAlPiUKICAgIGFycmFuZ2UoZGVzYyhjb3VudCkpCgphZ2dfY2l0aWVzIDwtIHJlcChOQSwgbnJvdyhjaXR5X2luZm8pKQpmb3IgKGkgaW4gMTpucm93KGNpdHlfaW5mbykpIHsKICAgIGFnZ19jaXRpZXNbaV0gPC0gY2l0eV9hZ2dyZWdhdGlvbihjaXR5X2luZm8kbG9jYXRpb25baV0pCn0gCgpjaXR5X2luZm9fYWdnIDwtIGNiaW5kKGNpdHlfaW5mbywgYWdnX2NpdGllcykKYGBgCgpgYGB7cn0KIyBTaG93aW5nIHRoZSB0b3AgdGVuIGNpdGllcyB0aGF0IGhhdmUgdGhlIG1vc3QgZGV2ZWxvcGVyIHN1cHBvcnQgOTk2LmljdQpjaXR5X2luZm9fYWdnICU+JSBncm91cF9ieShhZ2dfY2l0aWVzKSAlPiUKICAgIHN1bW1hcmlzZShjb3VudCA9IG4oKSkgJT4lCiAgICBmaWx0ZXIoYWdnX2NpdGllcyAhPSAiIiwgCiAgICAgICAgICAgYWdnX2NpdGllcyAhPSAiQ2hpbmEiKSAlPiUKICAgIGFycmFuZ2UoZGVzYyhjb3VudCkpICU+JQogICAgaGVhZCgxMCkKYGBgCgojIyMgMy4gU3VtbWFyeSBTdGF0aXN0aWNzIG9mIFN1cHBvcnRlcnMnIFJlbGF0ZWQgSW5mb3JtYXRpb24uIAoKYGBge3J9CiMgU3VtbWFyeSBzdGF0aXN0aWNzIG9mIHN1cHBvcnRlcnMnIGdpdGh1YiBhY2NvdW50LiAKZHRfdXNlcl9jbGQgJT4lIHNlbGVjdChmb2xsb3dlcnMsIGZvbGxvd2luZywgcHVibGljX3JlcG9zKSAlPiUKICAgIGdhdGhlcihzdGF0X3R5cGUsIG51bWJlciwgZm9sbG93ZXJzLCBmb2xsb3dpbmcsIHB1YmxpY19yZXBvcykgJT4lCiMgICAgZmlsdGVyKG51bWJlciA8PSAxMDAwMCkgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSBmYWN0b3Ioc3RhdF90eXBlKSwgeSA9IGxvZyhudW1iZXIpKSkgKwogICAgZ2VvbV9qaXR0ZXIoY29sb3IgPSAiZ3JleSIsIHdpZHRoID0gLjIpICsKICAgIGdlb21fYm94cGxvdChhbHBoYT0wLjYpICsKICAgIHN0YXRfc3VtbWFyeShmdW4ueSA9ICJtZWFuIiwgZ2VvbSA9ICJwb2ludCIsIHNpemUgPSA1LCBjb2xvciA9ICJyZWQiLCBzaGFwZSA9IDE1KSArCiAgICBsYWJzKHggPSAiU3VtbWFyeSBTdGF0aXN0aWNzIG9mIEZvbGxvd2VycywgRm9sbG93aW5nIGFuZCBQdWJsaWMgUmVwb3MsIE1lYW4gKHJlZCkiLCAKICAgICAgICAgeSA9ICJSZWxhdGl2ZSBWYWx1ZXMiKSArCiAgICBnZ3RpdGxlKCJTdW1tYXJ5IFN0YXRpc3RpY3MgUGxvdCAoTG9nIFRyYW5zZm9ybWVkKSIpCiAgICAKICAgIApgYGAKCiMjIyA0LiBEaXN0cmlidXRpb24gUGxvdCBvZiBTdXBwb3J0ZXJzJyBJbmZvcm1hdGlvbi4gCgpgYGB7cn0KIyBEaXN0cmlidXRpb24gZ3JhcGggb2Ygc3VwcG9ydGVyJ3MgZm9sbG93ZXJzIHVuZGVyIDUwLiAKZGlzdF9nZ3Bsb3QgPC0gZHRfdXNlcl9jbGQgJT4lIGZpbHRlcihmb2xsb3dlcnMgPD0gNTAsIGZvbGxvd2luZyA8PSA1MCwgcHVibGljX3JlcG9zIDw9IDUwKSAlPiUKICAgIGdncGxvdCgpICsKICAgIGdlb21fYmFyKGFlcyh4ID0gZm9sbG93ZXJzKSwgY29sPSJibGFjayIsIGZpbGw9ImJsYWNrIiwgYWxwaGE9MC41KSArCiAgICBnZW9tX2JhcihhZXMoeCA9IGZvbGxvd2luZyksIGNvbD0iYmxhY2siLCBmaWxsPSJyZWQiLCBhbHBoYT0wLjUpICsKICAgIGdlb21fYmFyKGFlcyh4ID0gcHVibGljX3JlcG9zKSwgY29sPSJibGFjayIsIGZpbGw9ImJsdWUiLCBhbHBoYT0wLjUpCiAgICAKZGlzdF9nZ3Bsb3QgKyAKICAgIGxhYnMoeCA9ICJGb2xsb3dlcnMgKEJsYWNrKSwgRm9sbG93aW5nIChSZWQpIGFuZCBQdWJsaWMgUmVwb3NpdG9yaWVzIChCbHVlKSIsIAogICAgICAgICB5ID0gIkNvdW50IikgKwogICAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIFBsb3QiKQpgYGAKCiMjIyA1LiBEaXN0cmlidXRpb24gUGxvdCBvZiBTdXBwb3J0ZXJzJyBSZWdpc3RyYXRpb24gRHVyYXRpb24uIAoKYGBge3J9CiMgQ2FsY3VsYXRlIHN1cHBvcnRlcnMnIG51bWJlciBvZiBkYXlzIHNpbmNlIHJlZ2lzdGVyZWQgdGhlIGdpdGh1YiBhY2NvdW50LiAKdG9kYXkgPC0gbHVicmlkYXRlOjp5bWQoIjIwMTktMDQtMjkiKQpkdF91c2VyX2NsZCA8LSBkdF91c2VyX2NsZCAlPiUKICAgICMgQ2FsY3VsYXRlIHRoZSBkdXJhdGlvbiBhbmQgY29udmVydCBpdCB0byBudW1lcmljYWwgdmFsdWUuIAogICAgbXV0YXRlKGR1cmF0aW9uID0gYXMubnVtZXJpYyhhcy5kdXJhdGlvbihpbnRlcnZhbChjcmVhdGVkX2F0LCB0b2RheSkpLCAiZGF5cyIpKQoKIyBTaG93aW5nIGF2ZXJhZ2UgcmVnaXN0cmF0aW9uIHllYXJzLiAKcHJpbnQobWVhbihkdF91c2VyX2NsZCRkdXJhdGlvbikvMzY1KQoKIyBEaXN0cmlidXRpb24gcGxvdCBvZiByZWdpc3RyYXRpb24gZGF5cy4gCmR0X3VzZXJfY2xkICU+JSBnZ3Bsb3QoYWVzKHggPSBkdXJhdGlvbi8zNjUpKSArIAogICAgZ2VvbV9oaXN0b2dyYW0oY29sPSJibGFjayIsIGZpbGw9ImdyZXkiLCBhbHBoYSA9IDAuNykgKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbWVhbihkdF91c2VyX2NsZCRkdXJhdGlvbikvMzY1LCBsaW5ldHlwZSA9ICJkb3R0ZWQiLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMS41KSArCiAgICBsYWJzKHkgPSAiRnJlcXVlbmN5IC8gQ291bnQiLCAKICAgICAgICAgeCA9ICJOdW1iZXIgb2YgWWVhcnMgU2luY2UgUmVnaXN0cmF0aW9uIikgKwogICAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIFBsb3Qgb2YgU3VwcG9ydGVycycgUmVnaXN0cmF0aW9uIER1cmF0aW9uIikKYGBgCgojIyAyLiBTdGF0aXN0aWNhbCBNb2RlbGluZwoKIyMjIDEuIEFuYWx5emluZyB0aGUgUmVsYXRpb25zaGlwIEJldHdlZW4gRm9sbG93ZXJzIGFuZCBvdGhlciBmYWN0b3JzLiAKCmBgYHtyfQojIFNlbGVjdCB2YXJpYWJsZXMgZm9yIGFuYWx5c2lzLiAKdXNlcl9zdGF0IDwtIGR0X3VzZXJfY2xkICU+JSAKICAgIHNlbGVjdChmb2xsb3dlcnMsIGZvbGxvd2luZywgcHVibGljX3JlcG9zLCBkdXJhdGlvbikKCiMgU2FuaWV0eSBDaGVjawp1c2VyX3N0YXQgJT4lIGhlYWQoMTApCmBgYAoKCmBgYHtyfQojIFVuc3VwZXJ2aXNlZCBMZWFybmluZzogUENBCnVzZXJfcGNhIDwtIHByY29tcCh1c2VyX3N0YXQsIGNlbnRlcj1UUlVFLCBzY2FsZS49VFJVRSkKcHJpbnQodXNlcl9wY2EpCnN1bW1hcnkodXNlcl9wY2EpCgojIFZpc3VhbGl6YXRpb24gdXNpbmcgMyBwcmluY2lwbGUgY29tcG9uZW50cyBQQ0EKcGxvdGx5KCkKYGBgCgoKIyMgMy4gTWFpbiBRdWVzdGlvbnMvSXNzdWVzIG9mIFN1cHBvcnRlcnMKCiMjIyAxLiBUcmVuZGluZyBJc3N1ZXMuIAoKYGBge3J9CiMgU2hvd2luZyB0b3AgdGVuIGlzc3VlcyB3aXRoIG1vc3QgY29tbWVudHMuIApkdF9pc3N1ZXNfY2xkICU+JSAKICAgIHNlbGVjdCh0aXRsZSwgY29tbWVudHMpICU+JSAKICAgIGFycmFuZ2UoZGVzYyhjb21tZW50cykpICU+JQogICAgaGVhZCgxMCkKCiMgUHN1ZWRvIHRvcCB0ZW4gaXNzdWVzICh0cmFuc2xhdGVkKQpwc3VlZG9faXNzdWVzIDwtIGRhdGEuZnJhbWUoCiAgICB0aXRsZSA9IGMoIkRpc2N1c3Npb24gVGhyZWFkIiwgCiAgICAgICAgICAgICAgIkFueSAnV29ya2luZyB1bmRlciA5OTYsIHNpY2tpbmcgaW4gSUNVJyB3YWxscGFwZXJzIHRvIHVzZT8iLCAKICAgICAgICAgICAgICAiQWZ0ZXJ3YXJkcywgSSBjb3VsZCBwdXQgJ3BhcnRpY2lwYXRlZCBpbiBhbiBvcGVuLXNvdXJjZSBwcm9qZWN0IHdpdGggb3ZlciAyMDAwKyBzdGFycycgb24gbXkgcmVzdW1lIiwgCiAgICAgICAgICAgICAgIkkgZG9uJ3QgdW5kZXJzdGFudCB0aGUgbGF3LCBidXQgSSdtIHdvbmRlcmluZyBpZiB0aGVyZSBpcyBhbnkgbGVnYWwgaXNzdWUgaW52b2x2ZWQ/IiwgCiAgICAgICAgICAgICAgIkNhbiB0aGlzIHJlcG9zaXRvcnkgYmUgaW4gdGhlIHRvcC10ZW4gc3RhcnMgbGlzdCBvbiBHaXRIdWI/IiwgCiAgICAgICAgICAgICAgIlN1YnN0YW50aWFsIHN1Z2dlc3Rpb25zIHJlZ2FyZGluZyB0aGUgYW50aS05OTYgbW92ZW1lbnRzLiIsIAogICAgICAgICAgICAgICJJdCdzIHVnbHkgdGhhdCB0aGUgZGV2ZWxvcGVycyB0YWtpbmcgc2FsYXJpZXMgd2hpbGUgY29tcGxhaW5pbmcgYWJvdXQgdGhlaXIgY29tcGFuaWVzLiIsIAogICAgICAgICAgICAgICJXb3JraW5nIG92ZXJ0aW1lIHRvbmlnaHQsIHdpbGwgZGVsZXRlIHRoZSBkYXRhYmFzZSB3aGVuIHRoaXMgcmVwbyByZWFjaCBvdmVyIDEwMGsgc3RhcnMiLCAKICAgICAgICAgICAgICAiQ3V0ZSBnaXJsIGJvcm4gaW4gMTk5NiBpcyBsb29raW5nIGZvciBkZXZlbG9wZXIgYm95ZnJpZW5kIG5vdy4iLCAKICAgICAgICAgICAgICAiV29yc2hpcCB0aGUgb3JpZ2luYWwgcG9zdCIpLCAKICAgIGNvbW1lbnRzID0gYygxMjQzLCA2MiwgNTMsIDM5LCAzNywgMzAsIDMwLCAyNiwgMjUsIDI0KSkgJT4lCiAgICBtdXRhdGUodGl0bGUgPSBhcy5jaGFyYWN0ZXIodGl0bGUpKQoKcHN1ZWRvX2lzc3VlcwpgYGAKCiMjIyAyLiBUZXh0IEFuYWx5c2lzIEFib3V0IFRvcGljcyBvZiA5OTYgTW92ZW1lbnQuCgpgYGB7cn0KIyBTZXR0aW5nIHRoZSB3b3JkLXNwbGl0IGVuZ2luZQpzcGxpdHRlciA8LSB3b3JrZXIoc3RvcF93b3JkID0gImRhdGEvc3RvcHdvcmRzLnR4dCIpCgojIFNwbGl0dGluZyB0aGUgd29yZHMuIApzZWcgPC0gYyhzcGxpdHRlcltkdF9pc3N1ZXNfY2xkJHRpdGxlXSwgc3BsaXR0ZXJbZHRfaXNzdWVzX2NsZCRib2R5XSkKc2VnIDwtIHNlZ1tuY2hhcihzZWcpID4gMV0KIyBFbmNvZGUgdGhlIGNoaW5lc2Ugd29yZCB2ZWN0b3IgYXMgVVRGLTggZm9ybWF0LiAKRW5jb2Rpbmcoc2VnKSA8LSAiVVRGLTgiCiMgRXh0cmFjdCB0aGUgdG9wIDEwMCAKc2VnX2RmIDwtIGRhdGEuZnJhbWUoc2VnID0gc2VnKSAlPiUKICAgIGdyb3VwX2J5KHNlZykgJT4lCiAgICBzdW1tYXJpc2UoZnJlcSA9IG4oKSkgJT4lCiAgICBhcnJhbmdlKGRlc2MoZnJlcSkpICU+JQogICAgaGVhZCgxMDApCgojIEdlbmVyYXRpbmcgd29yZCBjbG91ZCAoQ2hpbmVzZSBWZXJzaW9uKS4gCmZvbnRfZmFtaWx5IDwtIHBhcigiZmFtaWx5IikKcGFyKGZhbWlseSA9ICJBZG9iZSBIZWl0aSBTdGQgUiIpCndvcmRjbG91ZCh3b3Jkcz1zZWdfZGYkc2VnLCBmcmVxPXNlZ19kZiRmcmVxLCAKICAgICAgICAgIGNvbG9ycz1icmV3ZXIucGFsKDgsIkRhcmsyIiksIAogICAgICAgICAgc2NhbGU9Yyg0LCAwLjgpKQpgYGAKCgpgYGB7cn0KIyBMb2FkaW5nIHRyYW5zbGF0ZWQgZGF0YXNldC4gCnRyYW5zIDwtIHJlYWQudGFibGUoImRhdGEvdHJhbnNsYXRlIiwgc2VwPSJcdCIpWy0xLF0Kc2VnX2RmIDwtIGNiaW5kKHNlZ19kZiwgZW5nbCA9IHRyYW5zJFYyKQpgYGAKCmBgYHtyIHdhcm5pbmc9RkFMU0V9CiMgR2VuZXJhdGluZyB3b3JkIGNsb3VkIChFbmdsaXNoIFZlcnNpb24pCndvcmRjbG91ZCh3b3Jkcz1zZWdfZGYkZW5nbCwgZnJlcT1zZWdfZGYkZnJlcSwgCiAgICAgICAgICBjb2xvcnM9YnJld2VyLnBhbCg4LCJEYXJrMiIpLCAKICAgICAgICAgIHNjYWxlPWMoNCwgMC44KSkKYGBgCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoK